iBetter Books
수정

PART 02의 마무리는 실습입니다. 지금까지 배운 검출기 중 균형이 좋은 YuNet을 골라, 웹캠 영상에서 여러 얼굴을 실시간으로 검출하고 화면에 박스와 초당 프레임 수(FPS)를 함께 표시하는 작은 프로그램을 완성합니다. 이 한 편으로 "검출기를 영상 파이프라인에 끼워 넣는" 전체 흐름이 손에 익습니다.

무엇을 만드는가

만들 프로그램의 동작은 이렇습니다.

  • 웹캠을 열어 프레임을 계속 읽는다.
  • 각 프레임에서 YuNet으로 모든 얼굴을 검출한다.
  • 얼굴마다 박스와 신뢰도를 그리고, 검출된 얼굴 수와 FPS를 화면 위에 표시한다.
  • q 키를 누르면 깔끔하게 종료한다.

YuNet을 고른 이유는 앞 장의 비교에서 확인했듯 OpenCV 내장이라 추가 설치가 적고, 속도와 정확도의 균형이 좋으며, 랜드마크까지 함께 주기 때문입니다. 모델 파일 face_detection_yunet_2023mar.onnx만 작업 폴더에 준비합니다.

전체 코드

다음 내용을 live_detect.py로 저장합니다.

# 파일: live_detect.py"""YuNet으로 웹캠에서 다중 얼굴을 실시간 검출한다. q 키로 종료."""import timeimport cv2def main():    cap = cv2.VideoCapture(0)    if not cap.isOpened():        raise RuntimeError("웹캠을 열 수 없습니다. 카메라 연결과 권한을 확인하세요.")    # 첫 프레임으로 입력 크기를 파악한다    ok, frame = cap.read()    if not ok:        raise RuntimeError("첫 프레임을 읽지 못했습니다.")    h, w = frame.shape[:2]    # YuNet 검출기 준비: 모델, 빈 config, 입력 크기,    # score_threshold(0.8), nms_threshold(0.3), top_k(5000) 순서    detector = cv2.FaceDetectorYN.create(        "face_detection_yunet_2023mar.onnx", "", (w, h), 0.8, 0.3, 5000,    )    prev = time.perf_counter()    while True:        ok, frame = cap.read()        if not ok:            break        # 프레임 크기가 바뀔 수 있으니 매번 입력 크기를 맞춘다        h, w = frame.shape[:2]        detector.setInputSize((w, h))        _, faces = detector.detect(frame)        count = 0        if faces is not None:            count = len(faces)            for f in faces:                x, y, bw, bh = f[:4].astype(int)                score = f[-1]                cv2.rectangle(frame, (x, y), (x + bw, y + bh), (0, 255, 0), 2)                cv2.putText(frame, f"{score:.2f}", (x, y - 6),                            cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)        # FPS 계산: 직전 프레임과의 시간 차로 구한다        now = time.perf_counter()        fps = 1.0 / (now - prev) if now > prev else 0.0        prev = now        cv2.putText(frame, f"faces: {count}  fps: {fps:.1f}", (10, 28),                    cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2, cv2.LINE_AA)        cv2.imshow("live face detect - press q to quit", frame)        if cv2.waitKey(1) & 0xFF == ord("q"):            break    cap.release()    cv2.destroyAllWindows()if __name__ == "__main__":    main()

(face) 환경에서 실행합니다.

python live_detect.py

여러 사람이 카메라에 잡히면 각자 얼굴에 초록 박스와 신뢰도가 붙고, 좌측 상단에 검출된 얼굴 수와 FPS가 실시간으로 갱신됩니다.

실시간 다중 얼굴 검출 화면

코드에서 눈여겨볼 점

  • 입력 크기 갱신: YuNet은 입력 이미지 크기를 미리 알아야 해서, 루프 안에서 setInputSize를 매번 호출합니다. 창 크기나 카메라 해상도가 바뀌어도 안전하게 동작합니다.
  • FPS 측정: time.perf_counter로 직전 프레임과의 시간 차를 재서 역수를 취하면 초당 프레임 수가 됩니다. 검출기가 무거울수록 이 값이 떨어지므로, 다른 검출기로 바꿔 보며 체감 속도를 비교하기 좋습니다.
  • 예외 처리: 웹캠을 못 열거나 프레임을 못 읽는 상황을 RuntimeError로 분명히 알려, 검은 화면 앞에서 원인을 헤매지 않도록 했습니다.

실무 팁. 검출기를 바꿔 보고 싶다면 이 코드에서 YuNet 부분만 교체하면 됩니다. 예를 들어 검출 함수를 별도 함수로 분리해 두면, YuNet·MediaPipe·YOLO를 손쉽게 갈아 끼우며 같은 화면에서 FPS를 비교할 수 있습니다. 이렇게 "검출 부분만 갈아 끼우는" 구조로 짜 두면, 다음 PART에서 정렬과 인식을 이어 붙일 때도 코드를 거의 그대로 재사용할 수 있습니다.

이 장에서 기억할 것

YuNet을 웹캠 루프에 끼워 넣어 다중 얼굴을 실시간 검출하고, 박스·신뢰도·얼굴 수·FPS를 화면에 표시하는 프로그램을 완성했습니다. 입력 크기 갱신, FPS 측정, 예외 처리라는 영상 처리의 기본기를 함께 익혔고, 검출 부분만 갈아 끼우면 다른 검출기로 바로 바꿀 수 있는 구조를 만들었습니다. 이로써 PART 02가 끝납니다. 다음 PART 03에서는 검출한 얼굴을 똑바로 세우는 랜드마크와 정렬을 다뤄, 인식 정확도를 끌어올리는 결정적 중간 단계로 들어갑니다.